package cn.tedu.baking.aop;

import cn.tedu.baking.pojo.entity.Log;
import cn.tedu.baking.pojo.vo.UserVO;
import cn.tedu.baking.service.LogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Date;

/**
 *  日志的切面类
 */
@Aspect
@Component
public class LogAspect {
//    @Pointcut("@annotation(RequiredLog)")
//    public void LogPointCut(){}
    @Autowired
    LogService logService;
    @Around("@annotation(RequiredLog)") //通知内直接绑定切入点
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
        //记录(正常/异常)时间  / 记录状态值   / 记录异常信息
        int status = 1; //默认就是正常
        long time = 0L;//用来记录时间
        String error = "";//用来记录异常信息
        long start = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();//连接点方法,如果正常是有返回值的.
            long end = System.currentTimeMillis();
            time = end - start;//正常执行的耗时
            return result;
        } catch (Throwable throwable) {
            long errorEndTime = System.currentTimeMillis();
            time = errorEndTime - start;//异常的时间
            status = 0;//修改状态为0 代表异常
            error = throwable.getMessage();//记录异常的信息
            throw  throwable; //抛出异常信息
        }finally {
            saveLog(joinPoint,time,status,error);
        }

    }

    private void saveLog(ProceedingJoinPoint joinPoint, long time, int status, String error) throws NoSuchMethodException, JsonProcessingException {
        //1.获取用户: 我们可以获取此次线程中的请求对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //通过ServletRequestAttributes类型 中提供的getRequest() 方法 可以获取HttpServletRequest 对象
        HttpServletRequest request = requestAttributes.getRequest();
        HttpSession session = request.getSession();
        UserVO user = (UserVO) session.getAttribute("user");
        String username= "";
        if(user != null){
           username = user.getUserName(); //记录当前登录的用户
        }
        //2.可以通过请求对象获取到 ip
        String ip = request.getRemoteAddr();//通过请求对象获取 ip
        //3.获取方法的路径 (类的全包名.方法名) (反射)
        // 通过连接点打点getTarget().getClass()方法 获取的是这个连接点方法所在的类的class信息
        Class targetClass = joinPoint.getTarget().getClass();
        //joinPoint.getSignature()可以获取方法的签名对象(方法名称/方法的参数类型)
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = targetClass.getDeclaredMethod(signature.getName(), signature.getParameterTypes());//1.方法名   2. 方法的参数类型
        //类的全报名.方法名
        String method = targetClass.getName() +"."+targetMethod.getName();
        //4.获取操作(指的是 方法上注解中的value值)(反射)
        RequiredLog annotation = targetMethod.getAnnotation(RequiredLog.class);
        String operation = annotation.value();
        //5.获取参数列表
        //joinPoint.getArgs() 返回是Object数组类型的信息, 我们需要String类型可以通过writeValueAsString转换
        String params = new ObjectMapper().writeValueAsString(joinPoint.getArgs());
        //6.日志创建时间
        Log log = new Log();
        log.setTime(time);
        log.setError(error);
        log.setStatus(status);
        log.setMethod(method);
        log.setOperation(operation);
        log.setParams(params);
        log.setCreatedTime(new Date());
        log.setIp(ip);
        log.setUsername(username);
        logService.insertLog(log);
    }
}
